const net = require('net');
const iac = require('iac');
const socketState = {
    beforeLoginOLT: 1,
    afterLoginOLT: 2,
    backToRootLevel: 3,
}
const TIMEOUT = 50000;
const WAY = {
    COMMONTELENET: 1,
    CLOUDTElENET: 1 << 1,
    CLOUDSNMP: 1 << 2,
}
let sid = 0;

function CreateSocket(dest, cloudOltState) {
    let self = this;
    const lastLinehandler = {
        get: function (target, property) {
            return property in target ? target[property] : null;
        },
        set: function (obj, prop, value) {
            if (value.indexOf('name') !== -1 && self.state === socketState.beforeLoginOLT) {
                self.state = socketState.afterLoginOLT;
            }
            return Reflect.set(...arguments);
        }
    };
    this.socket = new net.Socket();
    this.socket.setTimeout(TIMEOUT);
    this.dest = dest;
    this.sid = sid++;
    this.index = 0;
    this.result = [];
    this.buff = '';
    this._cmd = {};
    this.cmdArr = [];
    this.quitOLT = [];
    this.busy = false;
    this.state = 0;
    this.cb = null;
    this.isConnect = false;
    this.lifeCycle = {};
    this.lastLine = new Proxy({
        value: ''
    }, lastLinehandler);
    this.cloudOltState = cloudOltState
}
CreateSocket.prototype.setCMDArr = function (cmdArr) {
    this._cmd = cmdArr
    this.quitOLT = cmdArr['quitOLT'];
}
CreateSocket.prototype.bindListener = function (eventName, type, fn) {
    this.socket[type](eventName, fn);
    return this;
}
CreateSocket.prototype.connect = function () {
    return new Promise((resolve, reject) => {
        try {

            // iac.on(this.socket, iac.will.echo, () => {
            //     console.log('接收到协商内容，iac.will,echo')
            //     this.socket.write(iac.dont.echo)
            //     this.socket.write(iac.dont.suppress)
            // })
            let str = ''
            this.socket.on('data', (data) => {
                str += data;
                if ((/User.*name/ig).test(str)) {
                    //telnet与olt通讯后期待收到username,状态变更为beforeLoginOLT
                    this.state = socketState.beforeLoginOLT;
                    this.socket.removeAllListeners('data')
                    this.bindListener('error', 'once', handler.call(this, 'error'))
                        .bindListener('connect', 'on', handler.call(this, 'connect'))
                        .bindListener('data', 'on', handler.call(this, 'data'))
                        .bindListener('end', 'on', handler.call(this, 'end'))
                    resolve(true)
                }
            })
            this.socket.on('error', (error) => {
                this.state = socketState.beforeLoginCloudOLT;
                reject(error);
            })
            this.socket.connect(this.dest.port, this.dest.hostname, () => {
                this.isConnect = true;
            });
            setTimeout(() => {
                console.log('connecting =', this.socket.connecting);
                if (this.socket.connecting) {
                    reject(new Error('socket connect timeout'))
                }
            }, TIMEOUT);
        } catch (error) {
            reject(error)
        }
    })

}
//quitOLT代表断开socket连接前，是否需要退出olt
CreateSocket.prototype.endSocket = async function (force, error) {
    this.socket.removeAllListeners('data')
    if (force) {
        console.log('endSocket 强制退出');
        return new Promise((resolve, reject) => {
            this.socket.removeAllListeners('data').removeAllListeners('error').removeAllListeners('connect').removeAllListeners('end');
            this.socket.destroy();
            this.isConnect = false;
            console.log(`连接的isConnect=${this.isConnect},sid=${this.sid}`);
            this.buff = '';
            this._cmd = {};
            this.cmdArr = [];
            this.busy = false;
            this.state = socketState.beforeLoginOLT;
            if (!error) {
                this.cb && this.cb(this.result || null)
            } else {
                this.errcb && this.errcb(error)
            }
            this.result = [];
            this.cb = null;
            resolve('Telnet Conntion is broker!');
        })
    }
    return new Promise((resolve, reject) => {
        try {
            if (this.cloudOltState && this.flag & WAY.CLOUDTElENET) {
                //如果是通过边际云连接olt那么需要退出olt然后断开socket
                console.log('如果是通过边际云连接olt那么需要退出olt然后断开socket');
                let currentCMD = this.quitOLT && this.quitOLT[0];
                let end = this.cloudOltState ? '\r\n' : currentCMD.send.indexOf('\r') === -1 ? '\r' : '';
                console.log(`看看现在currentCMD上有没有timer:${currentCMD.timer}`)
                if (!currentCMD.timer) {
                    currentCMD.timer = setTimeout(() => {
                        console.log('==============================')
                        console.log('退出olt超时，直接断开socket连接')
                        console.log('==============================')
                        //如果退出olt超时那么强行断开socket连接
                        this.socket.removeAllListeners('data').removeAllListeners('error').removeAllListeners('connect').removeAllListeners('end');
                        this.socket.destroy();
                        this.isConnect = false;
                        console.log(`连接的isConnect=${this.isConnect},sid=${this.sid}`);
                        this.buff = '';
                        this._cmd = {};
                        this.cmdArr = [];
                        this.busy = false;
                        this.state = socketState.beforeLoginCloudOLT;
                        this.cb && this.cb(this.result || null)
                        this.result = [];
                        this.cb = null;
                        resolve('close socket connection because timeout when logout olt.');
                    }, TIMEOUT)
                }
                this.socket.on('data', async data => {
                    let arr = data.toString().split('\r\n');
                    const lastLine = getLastLine(deleteBlankLine(arr));
                    if ((/log out/ig).test(lastLine)) {
                        if(this.socket.destroyed){
                            throw new Error('this.socket.destroyed' + this.socket.destroyed)
                        }
                        this.socket.write(`y${end}`)
                    } else if (lastLine.indexOf('Telnet Conntion is broker') !== -1 ||
                        lastLine.indexOf('Olt Connection is broken!') !== -1 ||
                        arr.filter(item => item.indexOf('Olt Connection is broken') !== -1).length > 0) {
                        console.log('==============================')
                        clearTimeout(currentCMD.timer);
                        console.log('已经退出olt了', currentCMD.timer)
                        console.log('==============================')
                        this.socket.removeAllListeners('data')
                        this.socket.destroy();
                        this.isConnect = false;
                        console.log(`连接的isConnect=${this.isConnect},sid=${this.sid}`);
                        this.buff = '';
                        this._cmd = {};
                        this.cmdArr = [];
                        this.busy = false;
                        this.state = socketState.beforeLoginCloudOLT;
                        this.cb && this.cb(this.result || null)
                        this.result = [];
                        this.cb = null;
                        resolve('log out olt --')
                    } else {
                        let matched;
                        if (currentCMD && currentCMD.expect instanceof RegExp) {
                            matched = currentCMD.expect.test(lastLine);
                        } else if (currentCMD && typeof currentCMD.expect === 'string') {
                            matched = lastLine.indexOf(currentCMD.expect) !== -1;
                        } else {
                            throw new Error('Expected a String or RegExp:' + currentCMD.expect)
                        }
                        if (matched) {
                            if(this.socket.destroyed){
                                throw new Error('this.socket.destroyed' + this.socket.destroyed)
                            }
                            console.log(`退出olt程序:${lastLine}===${currentCMD.send}`)
                            this.socket.write(`${currentCMD.send}${end}`);
                        }
                    }

                })
                if(this.socket.destroyed){
                    throw new Error('this.socket.destroyed' + this.socket.destroyed)
                }
                this.socket.write(`${currentCMD.send}${end}`);
            } else {
                //如果是直接连olt或者通过边际云拿snmp信息，那么直接断开socket连接即可
                console.log('如果是直接连olt或者通过边际云拿snmp信息，那么直接断开socket连接即可')
                this.socket.removeAllListeners('data').removeAllListeners('error').removeAllListeners('connect').removeAllListeners('end');
                this.socket.destroy();
                this.isConnect = false;
                this.buff = '';
                this._cmd = {};
                this.cmdArr = [];
                this.busy = false;
                this.state = socketState.beforeLoginOLT;
                this.cb && this.cb(this.result || null)
                this.result = [];
                this.cb = null;
                resolve('log out olt --')
            }
        } catch (error) {
            console.log(error)
            reject(error)
        }

    })
}
CreateSocket.prototype.getDest = function () {
    return this.dest;
}
CreateSocket.prototype.startCMD = function (cb, errcb, flag) {
    //判断当前状态,拼装好命令数组,把第一条发出去
    console.log('startCMD', this.state, flag);
    this.flag = flag;
    if (flag & WAY.COMMONTELENET) { //通过公网ip直连olt
        if (this.state === socketState.afterLoginOLT) { //目前连接是已经登录olt的状态
            this.lifeCycle = {
                beforeLoginOLT: null,
                afterLoginOLT: null,
                backToRootLevel: this._cmd['bussinessCMD'].length,
            }
            this.cmdArr = this._cmd['bussinessCMD']
                .concat(this._cmd['quitOLT'])
        } else { //目前socket还没有连接olt
            this.lifeCycle = {
                beforeLoginOLT: 0,
                afterLoginOLT: this._cmd['loginOLT'].length + this._cmd['management'].length - 1,
                backToRootLevel: this._cmd['loginOLT'].length + this._cmd['management'].length + this._cmd['bussinessCMD'].length,
            }
            this.cmdArr = this._cmd['loginOLT']
                .concat(this._cmd['management'])
                .concat(this._cmd['bussinessCMD'])
                .concat(this._cmd['quitOLT'])
        }
    } else if (flag & WAY.CLOUDTElENET) { //通过云端穿透连接olt
        if (this.state === socketState.beforeLoginOLT) {
            this.lifeCycle = {
                beforeLoginOLT: 0,
                afterLoginOLT: this._cmd['loginOLT'].length + this._cmd['management'].length - 1,
                backToRootLevel: this._cmd['loginOLT'].length + this._cmd['management'].length + this._cmd['bussinessCMD'].length,
            }
            this.cmdArr = this._cmd['loginOLT']
                .concat(this._cmd['management'])
                .concat(this._cmd['bussinessCMD'])
                .concat(this._cmd['quitOLT'])
        }
        if (this.state === socketState.afterLoginOLT) {
            this.lifeCycle = {
                beforeLoginOLT: null,
                afterLoginOLT: 0,
                backToRootLevel: this._cmd['bussinessCMD'].length,
            }
            this.cmdArr = this._cmd['bussinessCMD']
                .concat(this._cmd['quitOLT'])
        }
    } else if (flag & WAY.CLOUDSNMP) { //snmp通过云端穿透
        this.lifeCycle = {
            beforeLoginOLT: null,
            afterLoginOLT: null,
            backToRootLevel: null,
        }
        this.cmdArr = this._cmd['bussinessCMD']
    }
    // console.log('start cmd', this.cmdArr, this.index)
    this.buff = '';
    const currentCMD = this.cmdArr[0];
    this.index = 0;
    this.index++;
    let end = this.cloudOltState ? '\r\n' : currentCMD.send.indexOf('\r') === -1 ? '\r' : '';
    if(this.socket.destroyed){
        throw new Error('this.socket.destroyed' + this.socket.destroyed)
    }
    this.socket.write(`${currentCMD.send.trim()}${end}`)
    // console.log(this.index, '=', currentCMD.send)
    this.cb = cb;
    this.errcb = errcb;
    console.log('开始发送命令:', JSON.stringify(this.cmdArr))
}
CreateSocket.prototype.quitOltBaseLevel = function (cmdQuitOltBaseLevel) {
    this.cmdArr = cmdQuitOltBaseLevel;
    console.log('quitOltBaseLevel=============================')
    this.startCMD();
}
CreateSocket.prototype.clearCB = function () {
    this.cb = null;
}

function deleteBlankLine(arr) {
    return arr.filter(item => item.length > 0)
}

function getLastLine(arr) {
    let index = arr.findIndex(item => item.indexOf('Successful') !== -1 || item.indexOf('OK') != -1 || (/success/ig).test(item));
    if (index !== -1) {
        return arr[index] + ' ' + arr[arr.length - 1]
    } else {
        return arr[arr.length - 1]
    }
}

function handler(type) {
    const self = this;
    if (type === 'error') {
        return function (err) {
            console.log('发生了错误，即将关闭socket：', err);
            self.endSocket(true, err);
        }
    }
    if (type === 'connect') {
        return function () {
            console.log(`与${self.dest.hostname}建立连接成功`)
            self.isConnect = true;
        }
    }
    if (type === 'data') {
        async function handleData(data) {
            this.buff += data;
            let arr = data.toString().split('\r\n');
            const lastLine = getLastLine(deleteBlankLine(arr));
            if ((/More\s*/g).test(lastLine)) {
                if(this.socket.destroyed){
                    throw new Error('this.socket.destroyed' + this.socket.destroyed)
                }
                this.socket.write(` ${this.cloudOltState ? '\r\n':'\r'}`)
                return;
            }
            if (lastLine === 'Telnet Conntion is broker!') {
                await this.endSocket(true, new Error('Telnet Conntion is broker!'));
            }
            if (lastLine && lastLine.indexOf('Can not connect Olt') !== -1) {
                await this.endSocket(true, new Error('Can not connect Olt'));
            }
            this.lastLine.value = data.toString(); //记录收到的信息,用于判断是否已经连入边界云或者olt
            if (this.cmdArr.length === 0) {
                console.log('由于命令数组没有命令,退出', lastLine)
                return;
            }

            const currentCMD = this.index === this.cmdArr.length ? this.cmdArr[this.cmdArr.length - 1] : this.cmdArr[this.index];
            currentCMD.index = this.index;
            const preCMD = this.index === 0 ? null : this.cmdArr[this.index - 1];
            if (!currentCMD.timeout) {
                // 为发送项设置超时时间
                currentCMD.timeout = setTimeout(async () => {
                    await this.endSocket(true, new Error('Expect sequence timeout: ' + currentCMD.expect + '->' + currentCMD.send + '=>' + currentCMD.index));
                    // throw new Error('Expect sequence timeout: ' + currentCMD.expect + '->' + currentCMD.send+'=>'+currentCMD.index)
                }, TIMEOUT);
            }
            let end = this.cloudOltState ? '\r\n' : currentCMD.send.indexOf('\r') === -1 ? '\r' : '';
            // if(this.flag&WAY.CLOUDSNMP){
            //     if (this.index === this.cmdArr.length) {
            //         this.cb && this.cb(this.result)
            //         this.busy = false; //当前业务已经执行完毕了,所以改为空闲状态
            //         this.cmdArr.length = 0; //清空当前待执行的命令数组
            //         this.result = [];
            //         this.state = socketState.beforeLoginCloudOLT;
            //         return;
            //     }
            // }
            if (this.state === socketState.backToRootLevel) {
                console.log(`退出中。。。。。${lastLine}`)
                if ((/log out/ig).test(lastLine)) {
                    console.log('退多了，输入n，不要退出olt', this.state)
                    if(this.socket.destroyed){
                        throw new Error('this.socket.destroyed' + this.socket.destroyed)
                    }
                    this.socket.write(`n${end}`)
                } else if (!(/.*\(.*\).*/ig).test(lastLine) && (/#|>/).test(lastLine)) {
                    //已经成功的退到根路径了
                    clearTimeout(currentCMD.timeout);
                    console.log('已经成功的退到根路径了', lastLine)
                    if (this.index === this.cmdArr.length - 1) {
                        this.cb && this.cb(this.result)
                        this.busy = false; //当前业务已经执行完毕了,所以改为空闲状态
                        this.cmdArr.length = 0; //清空当前待执行的命令数组
                        this.result = [];
                        this.state = socketState.afterLoginOLT;
                        return;
                    }
                } else if ((/.*\(.*\).*/ig).test(lastLine)) {
                    if (currentCMD.expect.test(lastLine) || lastLine.indexOf('#') != -1 || lastLine.indexOf('>') != -1) {
                        if(this.socket.destroyed){
                            throw new Error('this.socket.destroyed' + this.socket.destroyed)
                        }
                        this.socket.write(`${currentCMD.send.trim()}${end}`);
                        console.log('当前lasteline=', lastLine, end)
                        console.log('由于既没有多退，也没有成功的退到根路径，再次发送命令', currentCMD.send)
                    } else {
                        console.log('虽然有括号但是currentcmd没有匹配上', currentCMD.expect, currentCMD.expect.toString())
                    }
                } else {
                    console.log('啥也没中', lastLine, (/.*\(.*\).*/ig).test(lastLine), currentCMD.expect.test(lastLine))
                    if (preCMD && preCMD.break && preCMD.fn) {
                        const bresult = preCMD.fn(this.buff);
                        if (bresult && !bresult.result) {
                            this.result.push(bresult.message);
                            await this.endSocket();
                        }
                    }
                    if (preCMD && preCMD.out) { //如果上一条cmd有out属性，说明收到的信息需要放到结果数组中返回
                        let str = deleteBlankLine(this.buff.split('\r\n')).join('\n')
                        console.log(`存储信息:${str}`)
                        this.result.push(str);
                    }
                }
            } else {
                let matched;
                if (currentCMD.expect instanceof RegExp) {
                    matched = currentCMD.expect.test(lastLine);
                    //console.log(`当前有没有匹配${matched}...${lastLine}`)
                    if (preCMD.out) {
                        clearTimeout(currentCMD.timeout);
                        currentCMD.timeout = setTimeout(async () => {
                            await this.endSocket(true, new Error('Expect sequence timeout: ' + currentCMD.expect + '->' + currentCMD.send + '=>' + currentCMD.index));
                        }, TIMEOUT);
                    }
                } else if (typeof currentCMD.expect === 'string') {
                    matched = lastLine.indexOf(currentCMD.expect) !== -1;
                } else {
                    throw new Error('Expected a String or RegExp:' + currentCMD.expect)
                }
                if (matched) {
                    // console.log('匹配', lastLine, matched, this.state === socketState.backToRootLevel)
                    clearTimeout(currentCMD.timeout);
                    //TODO 这里是对fn的处理,这里后边还需要确认下.先这样写着
                    if (preCMD && preCMD.break && preCMD.fn) {
                        const bresult = preCMD.fn(this.buff);
                        if (bresult && !bresult.result) {
                            this.result.push(bresult.message);
                            await this.endSocket();
                            return;
                        }
                    }
                    if (preCMD.timeout) {
                        clearTimeout(preCMD.timeout)
                    }
                    if (preCMD && preCMD.out) { //如果上一条cmd有out属性，说明收到的信息需要放到结果数组中返回
                        let str = deleteBlankLine(this.buff.split('\r\n')).join('\n')
                        console.log(`存储信息:${str}`)
                        this.result.push(str);
                    }
                    if (currentCMD.send) {
                        this.buff = '';
                        if (!currentCMD.backToRootLevel) {
                            if(this.socket.destroyed){
                                throw new Error('this.socket.destroyed' + this.socket.destroyed)
                            }
                            this.socket.write(`${currentCMD.send.trim()}${end}`)
                            console.log(`发送:${currentCMD.send}`)
                            this.index++;
                        }
                        if (currentCMD.send.indexOf('oltconnect') !== -1) {
                            currentCMD.timeout = setTimeout(async () => {
                                await this.endSocket(true, new Error('Expect sequence timeout: ' + currentCMD.expect + '->' + currentCMD.send + '=>' + currentCMD.index));
                                console.log('Expect sequence timeout: ' + currentCMD.expect + '->' + currentCMD.send + '=>' + currentCMD.index)
                            }, TIMEOUT);
                        }
                        if (currentCMD.backToRootLevel) {
                            //遇到backToRootLevel==true时，即将进入到退到根路径阶段
                            if ((/.*\(.*\).*/ig).test(lastLine)) {
                                //当前命令所在路径不是根路径，需要退出上一级
                                console.log('当前lastLine还不是根路径，所以需要退到上一层', this.state);
                                if(this.socket.destroyed){
                                    throw new Error('this.socket.destroyed' + this.socket.destroyed)
                                }
                                this.socket.write(`${currentCMD.send.trim()}${end}`)
                                console.log(`发送:${currentCMD.send}`)
                            } else {
                                console.log('当前已经在根目录下了，不用退到上一层')
                                clearTimeout(currentCMD.timeout);
                                this.index++;
                                if (this.index === this.cmdArr.length - 1) {
                                    this.cb && this.cb(this.result)
                                    this.busy = false; //当前业务已经执行完毕了,所以改为空闲状态
                                    this.cmdArr.length = 0; //清空当前待执行的命令数组
                                    this.result = [];
                                    this.state = socketState.afterLoginOLT;
                                    return;
                                }
                            }
                        }
                        for (let key in this.lifeCycle) {
                            if (this.index === this.lifeCycle[key]) {
                                this.state = socketState[key];
                            }
                        }
                    }

                }
            }


        }
        return handleData.bind(this);
    }
    if (type === 'end') {
        return function () {
            console.log(`--end--`)
            self.endSocket(true);
            self.isConnect = false;
        }
    }
    if (type === 'timeout') {
        let self = this;
        //timeout事件代表当前socket连接是空闲了，那么这时我们把链接关闭
        return function () {
            self.endSocket(true);
            console.log('timeout,当前套接字空闲')
        }
    }
    if (type === closed) {
        let self = this;
        return function () {
            console.log('触发closed事件');
            self.isConnect = false;
        }
    }
}

module.exports = CreateSocket